package com.lion.common.modules.file.service.assistants;

import lombok.Data;

import java.io.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Base64;
import java.util.UUID;

/**
 * @author Chris Chan
 * Create On 2025/9/1 下午4:53
 * Use for:
 * Explain:
 */
@Data
public class SeaweedFSClient {
    private String filerUrl;
    private String masterUrl;
    private String remotePath;
    private final HttpClient httpClient;

    /**
     * 构造方法
     *
     * @param filerUrl
     * @param masterUrl
     */
    public SeaweedFSClient(String filerUrl, String masterUrl, String remotePath) {
        this.filerUrl = normalizeUrl(filerUrl);
        this.masterUrl = normalizeUrl(masterUrl);
        this.remotePath = normalizePath(remotePath);

        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(15))
                .followRedirects(HttpClient.Redirect.NORMAL)
                .build();
    }

    /**
     * 创建SeaweedFSClient实例
     *
     * @param filerUrl
     * @param masterUrl
     * @return
     */
    public static SeaweedFSClient create(String filerUrl, String masterUrl, String remotePath) {
        return new SeaweedFSClient(filerUrl, masterUrl, remotePath);
    }

    /**
     * 规范URL
     *
     * @param url
     * @return
     */
    private String normalizeUrl(String url) {
        if (url == null) return "";
        url = url.trim();
        return url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
    }

    /**
     * 规范路径
     *
     * @param path
     * @return
     */
    private String normalizePath(String path) {
        if (path == null) return "";
        path = path.trim();
        while (path.startsWith("/")) {
            path = path.substring(1);
        }
        return path;
    }

    /**
     * 生成边界
     *
     * @return
     */
    private String generateBoundary() {
        return "----SeaweedFSBoundary" + UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 构建多部分表单数据
     *
     * @param fileContent
     * @param filename
     * @param boundary
     * @return
     * @throws IOException
     */
    private byte[] buildMultipartFormData(byte[] fileContent, String filename, String boundary) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), true);

        writer.append("--").append(boundary).append("\r\n");
        writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"").append(filename).append("\"\r\n");
        writer.append("Content-Type: ").append(getMimeType(filename)).append("\r\n");
        writer.append("\r\n");
        writer.flush();

        outputStream.write(fileContent);
        outputStream.write("\r\n".getBytes(StandardCharsets.UTF_8));

        writer.append("--").append(boundary).append("--").append("\r\n");
        writer.flush();

        return outputStream.toByteArray();
    }

    /**
     * 获取文件名
     *
     * @param path
     * @return
     */
    private String getFileName(String path) {
        if (path == null) return "file";
        int lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
        return lastSlash >= 0 && lastSlash < path.length() - 1 ? path.substring(lastSlash + 1) : path;
    }

    /**
     * 获取文件MIME类型
     *
     * @param filename
     * @return
     */
    private String getMimeType(String filename) {
        if (filename == null) return "application/octet-stream";
        String extension = filename.toLowerCase();
        if (extension.contains(".")) {
            extension = extension.substring(extension.lastIndexOf(".") + 1);
        }

        return switch (extension) {
            case "txt" -> "text/plain";
            case "jpg", "jpeg" -> "image/jpeg";
            case "png" -> "image/png";
            case "gif" -> "image/gif";
            case "pdf" -> "application/pdf";
            default -> "application/octet-stream";
        };
    }

    /**
     * 上传本地文件
     *
     * @param localFilePath
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public String uploadLocalFile(String localFilePath, String remotePath) throws IOException, InterruptedException {
        File file = new File(localFilePath);
        if (!file.exists()) {
            throw new FileNotFoundException("本地文件不存在: " + localFilePath);
        }

        byte[] fileContent = Files.readAllBytes(file.toPath());
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        String boundary = generateBoundary();
        byte[] multipartData = buildMultipartFormData(fileContent, getFileName(remotePath), boundary);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .header("Content-Type", "multipart/form-data; boundary=" + boundary)
                .POST(HttpRequest.BodyPublishers.ofByteArray(multipartData))
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() == 201 || response.statusCode() == 200) {
            return normalizedPath;
        } else {
            throw new IOException("上传失败: " + response.statusCode() + " - " + response.body());
        }
    }

    /**
     * 上传字节数组
     *
     * @param content
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public String uploadBytes(byte[] content, String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        String boundary = generateBoundary();
        byte[] multipartData = buildMultipartFormData(content, getFileName(remotePath), boundary);

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .header("Content-Type", "multipart/form-data; boundary=" + boundary)
                .POST(HttpRequest.BodyPublishers.ofByteArray(multipartData))
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() == 201 || response.statusCode() == 200) {
            return normalizedPath;
        } else {
            throw new IOException("上传失败: " + response.statusCode() + " - " + response.body());
        }
    }

    /**
     * 上传文本
     *
     * @param text
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */

    public String uploadText(String text, String remotePath) throws IOException, InterruptedException {
        return uploadBytes(text.getBytes(StandardCharsets.UTF_8), remotePath);
    }

    /**
     * 上传Base64编码的字符串
     *
     * @param base64Content
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public String uploadBase64(String base64Content, String remotePath) throws IOException, InterruptedException {
        byte[] content = Base64.getDecoder().decode(base64Content);
        return uploadBytes(content, remotePath);
    }

    /**
     * 下载为字节数组
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public byte[] downloadToBytes(String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .GET()
                .build();

        HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
        if (response.statusCode() == 200) {
            return response.body();
        } else {
            throw new IOException("下载失败: " + response.statusCode());
        }
    }

    /**
     * 下载到本地文件
     *
     * @param remotePath
     * @param localFilePath
     * @throws IOException
     * @throws InterruptedException
     */
    public void downloadToLocal(String remotePath, String localFilePath) throws IOException, InterruptedException {
        byte[] content = downloadToBytes(remotePath);
        Files.write(Paths.get(localFilePath), content);
    }

    /**
     * 下载为文本
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public String downloadToText(String remotePath) throws IOException, InterruptedException {
        byte[] content = downloadToBytes(remotePath);
        return new String(content, StandardCharsets.UTF_8);
    }

    /**
     * 下载为Base64编码的字符串
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public String downloadToBase64(String remotePath) throws IOException, InterruptedException {
        byte[] content = downloadToBytes(remotePath);
        return Base64.getEncoder().encodeToString(content);
    }


    /**
     * 删除文件
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public boolean deleteFile(String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .DELETE()
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        return response.statusCode() == 202 || response.statusCode() == 200;
    }

    /**
     * 检查文件是否存在（修复HEAD请求）
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public boolean fileExists(String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        // 修复：使用正确的HEAD请求方式
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .method("HEAD", HttpRequest.BodyPublishers.noBody())  // 正确的方式
                .build();

        HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
        return response.statusCode() == 200;
    }

    /**
     * 获取文件大小（修复HEAD请求）
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public long getFileSize(String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        // 修复：使用正确的HEAD请求方式
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .method("HEAD", HttpRequest.BodyPublishers.noBody())  // 正确的方式
                .build();

        HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
        if (response.statusCode() == 200) {
            return response.headers().firstValue("Content-Length")
                    .map(Long::parseLong)
                    .orElse(-1L);
        }
        return -1L;
    }

    /**
     * 修复：获取文件信息（包括MIME类型）
     *
     * @param remotePath
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public String getFileInfo(String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .method("HEAD", HttpRequest.BodyPublishers.noBody())
                .build();

        HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());
        if (response.statusCode() == 200) {
            return String.format("Size: %s, Type: %s",
                    response.headers().firstValue("Content-Length").orElse("unknown"),
                    response.headers().firstValue("Content-Type").orElse("unknown"));
        }
        return "File not found";
    }

    /**
     * 修复：健康检查
     *
     * @return
     */
    public boolean healthCheck() {
        try {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(filerUrl + "/"))
                    .GET()
                    .build();
            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            return response.statusCode() == 200;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 修复：检查路径是否存在
     *
     * @param remotePath
     * @throws IOException
     * @throws InterruptedException
     */
    public void checkPath(String remotePath) throws IOException, InterruptedException {
        String normalizedPath = normalizePath(remotePath);
        String fullUrl = filerUrl + "/" + normalizedPath;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(fullUrl))
                .GET()
                .build();

        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("路径检查 - 状态码: " + response.statusCode());
    }

    /**
     * 获取文件URL
     *
     * @param remotePath
     * @return
     */
    public String getFileUrl(String remotePath) {
        return filerUrl + "/" + normalizePath(remotePath);
    }

    /**
     * 获取远程路径
     *
     * @param fileName
     * @return
     */
    public String getRemotePath(String fileName) {
        return remotePath + "/" + fileName;
    }
}
